﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Net;
using System.Timers;

namespace Chat
{
    class Client
    {
        Socket socket;
        AsyncCallback connectCallBack;
        string clientName = "unknown";
        bool identified;
        Timer identifyTimer;
        byte[] buffer;
        const int MAX_BUFFER_SIZE = 64;
        List<byte> receivedData;
        DateTime connectTime;


        public byte[] Buffer
        {
            get { return buffer; }
            set { buffer = value; }
        }
        public List<byte> ReceivedData
        {
            get { return receivedData; }
            set { receivedData = value; }
        }

        public Socket Socket
        {
            get { return socket; }
            set { socket = value; }
        }
        public string ClientName
        {
            get { return clientName; }
            set { clientName = value; }
        }
        public bool Identified
        {
            get { return identified; }
            set { identified = value; }
        }

        public Timer IdentifyTimer
        {
            get { return identifyTimer; }
            set { identifyTimer = value; }
        }
        public DateTime ConnectTime
        {
            get { return connectTime; }
            set { connectTime = value; }
        }




        public Client()
        {
            socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            connectCallBack = new AsyncCallback(Connecting);
            buffer = new byte[MAX_BUFFER_SIZE];
            receivedData = new List<byte>();
            identified = false;
            identifyTimer = new Timer();
        }

        public void ConnectToServer(string ipIn, int port)
        {
            IPEndPoint endPoint = new IPEndPoint(IPAddress.Parse(ipIn), port);
            socket.BeginConnect(endPoint, connectCallBack, null);
        }

        public void Connecting(IAsyncResult aResult)
        {
            socket.EndConnect(aResult);

            //display in the client panel that we've connected
            ChatMessage temp = new ChatMessage("##You have connected to " + ((IPEndPoint)(socket.RemoteEndPoint)).Address.ToString());
            ClientMessage(this, new ChatEventArgs(temp));

            //Identify ourselves to the server
            CommandMessage cmd = new CommandMessage(CommandMessage.CommandType.Identify, clientName);
            SendMessage(cmd);
            socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, MessageReceived, null);
        }


        public event EventHandler ClientMessage = delegate { };
        void MessageReceived(IAsyncResult asyn)
        {

            if (socket.Connected)
            {
                int bytesReceived = socket.EndReceive(asyn);
                if (bytesReceived > 0)
                {
                    receivedData.AddRange(buffer);

                    //if there are still bytes available to be read
                    //then we havent received all the bytes yet
                    if (socket.Available == 0)
                    {
                        Message receivedMsg = Message.Deserialize(receivedData.ToArray(), receivedData.ToArray().Length);
                        ProcessMessage(receivedMsg);
                        receivedData.Clear();
                    }
                    socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, MessageReceived, null);
                }
                else
                {
                    socket.Shutdown(SocketShutdown.Both);
                    socket.Close();
                }
            }
        }

        private void ProcessMessage(Message receivedMsg)
        {
            if (receivedMsg.GetType() == typeof(ChatMessage))
            {
                ChatMessage msg = (ChatMessage)receivedMsg;
                ClientMessage(this, new ChatEventArgs(msg));
            }
            else
            {
                throw new Exception("Unknown Message Type!");
            }



        }

        internal void SendMessage(Message msgIn)
        {
            socket.Send(msgIn.Serialize());

        }

        internal void Disconnect()
        {
            //sock.Close(3);
            socket.Shutdown(SocketShutdown.Both);
            socket.Disconnect(false);

        }
    }
}
